wip: Add more information to crossing events
authorMatthias Clasen <mclasen@redhat.com>
Wed, 19 Feb 2020 04:30:16 +0000 (23:30 -0500)
committerMatthias Clasen <mclasen@redhat.com>
Fri, 21 Feb 2020 05:51:03 +0000 (00:51 -0500)
Add fields for direct descendents to GtkCrossingData,
and populate them when emitting focus change events.

Also add accessors for these fields to GtkEventControllerKey,
and verify that they are set properly in the focus test.

Not done yet: Do the same for pointer crossing events.

gtk/gtkeventcontroller.h
gtk/gtkeventcontrollerkey.c
gtk/gtkeventcontrollerkey.h
gtk/gtkwindow.c

index 38569453f14f32b35aa9527b2a144438263771ca..2cd8167c0353400ba8c4d157e9f07b4d1d37bd9c 100644 (file)
@@ -48,10 +48,15 @@ typedef struct _GtkCrossingData GtkCrossingData;
  * @direction: whether this is a focus-in or focus-out event
  * @mode: the crossing mode
  * @old_target: the old target
+ * @old_descendent: the direct child of the receiving widget that
+ *     is an ancestor of @old_target, or %NULL if @old_target is not
+ *     a descendent of the receiving widget
  * @new_target: the new target
+ * @new_descendent: the direct child of the receiving widget that
+ *     is an ancestor of @new_target, or %NULL if @new_target is not
+ *     a descendent of the receiving widget
  *
- * The struct that is passed to gtk_event_controller_handle_crossing()
- * and is also passed to #GtkEventControllerKey::focus-change.
+ * The struct that is passed to gtk_event_controller_handle_crossing().
  *
  * The @old_target and @new_target fields are set to the old or new
  * focus or hover location.
@@ -61,7 +66,9 @@ struct _GtkCrossingData {
   GtkCrossingDirection direction;
   GdkCrossingMode mode;
   GtkWidget *old_target;
+  GtkWidget *old_descendent;
   GtkWidget *new_target;
+  GtkWidget *new_descendent;
 };
 
 GDK_AVAILABLE_IN_ALL
index a8a5912d336503b7b818b1cde65b1088f7b208be..14a79e7b0cc9fd7442c751e195f5af3fe95e2ba4 100644 (file)
@@ -548,6 +548,24 @@ gtk_event_controller_key_get_focus_target (GtkEventControllerKey *controller)
   return controller->current_crossing->new_target;
 }
 
+GtkWidget *
+gtk_event_controller_key_get_old_focus_child (GtkEventControllerKey *controller)
+{
+  g_return_val_if_fail (GTK_IS_EVENT_CONTROLLER_KEY (controller), NULL);
+  g_return_val_if_fail (controller->current_crossing != NULL, NULL);
+
+  return controller->current_crossing->old_descendent;
+}
+
+GtkWidget *
+gtk_event_controller_key_get_new_focus_child (GtkEventControllerKey *controller)
+{
+  g_return_val_if_fail (GTK_IS_EVENT_CONTROLLER_KEY (controller), NULL);
+  g_return_val_if_fail (controller->current_crossing != NULL, NULL);
+
+  return controller->current_crossing->new_descendent;
+}
+
 /**
  * gtk_event_controller_key_contains_focus:
  * @self: a #GtkEventControllerKey
index 755947d255bb234749bfe27bb13ff2f5c11b909e..4aa22777d69ff4665b297177c62253a4299b28df 100644 (file)
@@ -62,6 +62,10 @@ GDK_AVAILABLE_IN_ALL
 GtkWidget *         gtk_event_controller_key_get_focus_origin   (GtkEventControllerKey  *controller);
 GDK_AVAILABLE_IN_ALL
 GtkWidget *         gtk_event_controller_key_get_focus_target   (GtkEventControllerKey  *controller);
+GDK_AVAILABLE_IN_ALL
+GtkWidget *         gtk_event_controller_key_get_old_focus_child (GtkEventControllerKey  *controller);
+GDK_AVAILABLE_IN_ALL
+GtkWidget *         gtk_event_controller_key_get_new_focus_child (GtkEventControllerKey  *controller);
 
 GDK_AVAILABLE_IN_ALL
 gboolean            gtk_event_controller_key_contains_focus     (GtkEventControllerKey  *self);
index 736d76441b108b91682e51cf268db290f4f7aa8b..d399d3c7d7d42772ffa9b003f10d57b920208b18 100644 (file)
@@ -6346,15 +6346,49 @@ gtk_window_move_focus (GtkWidget        *widget,
     gtk_window_set_focus (GTK_WINDOW (widget), NULL);
 }
 
+static void
+check_crossing_invariants (GtkWidget *widget,
+                           GtkCrossingData *crossing)
+{
+  if (crossing->old_target == NULL)
+    g_assert (crossing->old_descendent == NULL);
+  else if (crossing->old_descendent == NULL)
+    g_assert (crossing->old_target == widget || !gtk_widget_is_ancestor (crossing->old_target, widget));
+  else
+    {
+      g_assert (gtk_widget_get_parent (crossing->old_descendent) == widget);
+      g_assert (gtk_widget_is_ancestor (crossing->old_descendent, widget));
+      g_assert (crossing->old_target == crossing->old_descendent || gtk_widget_is_ancestor (crossing->old_target, crossing->old_descendent));
+    }
+  if (crossing->new_target == NULL)
+    g_assert (crossing->new_descendent == NULL);
+  else if (crossing->new_descendent == NULL)
+    g_assert (crossing->new_target == widget || !gtk_widget_is_ancestor (crossing->new_target, widget));
+  else
+    {
+      g_assert (gtk_widget_get_parent (crossing->new_descendent) == widget);
+      g_assert (gtk_widget_is_ancestor (crossing->new_descendent, widget));
+      g_assert (crossing->new_target == crossing->new_descendent || gtk_widget_is_ancestor (crossing->new_target, crossing->new_descendent));
+    }
+}
+
 static void
 synthesize_focus_change_events (GtkWindow *window,
                                 GtkWidget *old_focus,
                                 GtkWidget *new_focus)
 {
   GtkCrossingData crossing;
+  GtkWidget *ancestor;
   GtkWidget *widget, *focus_child;
   GList *list, *l;
   GtkStateFlags flags;
+  GtkWidget *prev;
+  gboolean seen_ancestor;
+
+  if (old_focus && new_focus)
+    ancestor = gtk_widget_common_ancestor (old_focus, new_focus);
+  else
+    ancestor = NULL;
 
   flags = GTK_STATE_FLAG_FOCUSED;
   if (gtk_window_get_focus_visible (GTK_WINDOW (window)))
@@ -6363,29 +6397,52 @@ synthesize_focus_change_events (GtkWindow *window,
   crossing.type = GTK_CROSSING_FOCUS;
   crossing.mode = GDK_CROSSING_NORMAL;
   crossing.old_target = old_focus;
+  crossing.old_descendent = NULL;
   crossing.new_target = new_focus;
+  crossing.new_descendent = NULL;
 
   crossing.direction = GTK_CROSSING_OUT;
 
+  prev = NULL;
+  seen_ancestor = FALSE;
   widget = old_focus;
   while (widget)
     {
+      crossing.old_descendent = prev;
+      if (seen_ancestor)
+        {
+          crossing.new_descendent = new_focus ? prev : NULL;
+        }
+      else if (widget == ancestor)
+        {
+          GtkWidget *w;
+
+          crossing.new_descendent = NULL;
+          for (w = new_focus; w != ancestor; w = gtk_widget_get_parent (w))
+            crossing.new_descendent = w;
+
+          seen_ancestor = TRUE;
+        }
+      else
+        {
+          crossing.new_descendent = NULL;
+        }
+      
+      check_crossing_invariants (widget, &crossing);
       gtk_widget_handle_crossing (widget, &crossing, 0, 0);
       gtk_widget_unset_state_flags (widget, flags);
       gtk_widget_set_focus_child (widget, NULL);
+      prev = widget;
       widget = gtk_widget_get_parent (widget);
     }
 
   list = NULL;
-  widget = new_focus;
-  while (widget)
-    {
-      list = g_list_prepend (list, widget);
-      widget = gtk_widget_get_parent (widget);
-    }
+  for (widget = new_focus; widget; widget = gtk_widget_get_parent (widget))
+    list = g_list_prepend (list, widget);
 
   crossing.direction = GTK_CROSSING_IN;
 
+  seen_ancestor = FALSE;
   for (l = list; l; l = l->next)
     {
       widget = l->data;
@@ -6393,6 +6450,29 @@ synthesize_focus_change_events (GtkWindow *window,
         focus_child = l->next->data;
       else
         focus_child = NULL;
+
+      crossing.new_descendent = focus_child;
+      if (seen_ancestor)
+        {
+          crossing.old_descendent = NULL;
+        }
+      else if (widget == ancestor)
+        {
+          GtkWidget *w;
+
+          crossing.old_descendent = NULL;
+          for (w = old_focus; w != ancestor; w = gtk_widget_get_parent (w))
+            {
+              crossing.old_descendent = w;
+            }
+
+          seen_ancestor = TRUE;
+        }
+      else
+        {
+          crossing.old_descendent = old_focus ? focus_child : NULL;
+        }
+      check_crossing_invariants (widget, &crossing);
       gtk_widget_handle_crossing (widget, &crossing, 0, 0);
       gtk_widget_set_state_flags (widget, flags, FALSE);
       gtk_widget_set_focus_child (widget, focus_child);